package titt;

import titt.config.ValueConfig;
import titt.io.IOFactory;
import titt.io.encoding.EncodingFacade;
import titt.service.LinkedRegistrationStrategy;
import titt.service.RegistrationStrategy;
import titt.service.ServiceMechanics;
import titt.service.invoke.StandardMethodParser;
import titt.service.invoke.UnknownMethodRedirect;
import titt.service.proxy.CombinedInvoker;
import titt.service.proxy.NuderInvoker;
import titt.service.proxy.TITTInvoker;
import titt.service.proxy.TITTProxyGenerator;
import titt.service.strategy.CombinedStrategy;
import titt.service.strategy.NuderStrategy;
import titt.service.strategy.RPCStrategy;
import titt.service.strategy.WebServer;
import titt.type.Types;
import titt.util.deploy.TITTWebServer;

public class TITT {

	protected TITT() {}
	
	private static RegistrationStrategy rpc = new LinkedRegistrationStrategy(
			new ServiceMechanics(new CombinedStrategy(new RPCStrategy(), new NuderStrategy()))
		);
	
	private static TITTProxyGenerator gen = new TITTProxyGenerator();
	private static TITTWebServer web;
	private static EncodingFacade ef = IOFactory.createEncodingFacade();
	
	public static void config(int port, String host) {
		ValueConfig.get().setPort(port);
		ValueConfig.get().setHost(host);
	}
	
	public static String encodeURL(String url) {
		return ef.encodeToString(Types.STRING, url);
	}
	
	public static String decodeURL(String url) {
		return (String)ef.decodeFromString(Types.STRING, url);
	}
		
	public static void startWebServer(String base, WebServer fallback) {
		if(web == null)
			web = new TITTWebServer(base, fallback);
		TITT.register(web, "*");
	}
	
	public static void startWebServer(WebServer fallback) {
		startWebServer("", fallback);
	}
	
	public static void startWebServer(String base) {
		startWebServer(base, null);
	}
	
	public static void startWebServer() {
		startWebServer("", null);
	}
	
	public static TITTWebServer getWebServer() {
		return web;
	}

	/*
	 * Registration / Unregistration of services
	 */
	
	public static void register(Object o, String... tokens) {
		try {
			rpc.register(o, new UnknownMethodRedirect("get", new StandardMethodParser()), tokens);
		}
		catch(Exception e) {
			throw new RuntimeException(e);
		}
	}	
	
	public static void unregister(Object o) {
		unregister(o, false);
	}
	
	public static void unregister(Object o, boolean lazy) {
		try {
			rpc.unregister(o, lazy);
		}
		catch(Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	
	/*
	 * Proxy generation
	 */
	
	public static <T> T createProxy(String t0, T[]... empty) {
		return createProxy(getType(empty), t0);
	}
	
	public static <T> T createProxy(String t0, String t1, T[]... empty) {
		return createProxy(getType(empty), t0, t1);
	}
	
	public static <T> T createProxy(String t0, String t1, String t2, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2);
	}

	public static <T> T createProxy(String t0, String t1, String t2, String t3, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2, t3);
	}

	public static <T> T createProxy(String t0, String t1, String t2, String t3, String t4, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2, t3, t4);
	}

	public static <T> T createProxy(String t0, String t1, String t2, String t3, String t4, String t5, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2, t3, t4, t5);
	}

	public static <T> T createProxy(String t0, String t1, String t2, String t3, String t4, String t5, String t6, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2, t3, t4, t5, t6);
	}

	public static <T> T createProxy(String t0, String t1, String t2, String t3, String t4, String t5, String t6, String t7, T[]... empty) {
		return createProxy(getType(empty), t0, t1, t2, t3, t4, t5, t6, t7);
	}

	public static <T> T createProxy(Class<T> c, String... tokens) {
		try {
			return gen.createGenericTITTProxy(c, new CombinedInvoker(new NuderInvoker(tokens), new TITTInvoker(tokens)));
		}
		catch(Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@SuppressWarnings("unchecked")
	private static <T> Class<T> getType(T[]... t) {
		return (Class<T>)t.getClass().getComponentType().getComponentType();
	}

}
